package com.atguigu.spring5.dynamicProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

/**
 * 通过jdk动态代理 给UserDao中的方法进行功能增强
 */
public class JDKProxy {

    public static void main(String[] args) {
        // 创建接口实现类代理对象
        Class[] interfaces = {UserDao.class};
//        Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
//            // 通过匿名内部类的方式  实现 InvocationHandler 接口
//            // 也可以再写一个实体类，再调用一下
//            @Override
//            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//                return null;
//            }
//        });
        UserDaoImpl userDao = new UserDaoImpl();
        // 方法有三个参数：
        //第一参数，类加载器。
        //第二参数，增强方法所在的类，这个类实现的接口，支持填写多个接口。
        //第三参数，代理对象实例（指实现了接口 InvocationHandler （调用处理） ，对被代理对象进行增强处理的"代理对象"。）
        // 相当于 UserDao dao = new UserDaoImpl();  只是这里是通过被代理的方式获取。即 接口 = 实现类的代理对象
        UserDao dao = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
        int result = dao.add(1,2);
        System.out.println(String.format("执行结果为%s",result));
    }
}

// 一个.java文件中可以有很多类。不过注意以下几点：
//1、public 权限的类只能有一个（也可以一个都没有，但最多只有1个）
//2、这个.java文件的文件名必须是public类的类名（一般的情况下，这里放置main方法是程序的入口。）
//3、若这个文件中没有public的类，则文件名随便是一个类的名字即可
//4、用javac 编译这个.java文件的时候，它会给每一个类生成一个.class文件
class UserDaoProxy implements InvocationHandler{
    // 1 将被代理的对象，传递进来
    // 通过有参构造传递
    private Object obj;
    //public UserDaoProxy(UserDaoImpl obj){
    public UserDaoProxy(Object obj){
        // 使用Object 更通用
        this.obj = obj;

    }

    // 2 在invoke中做增强逻辑
    /**
     * 只要 UserDaoProxy 被创建，那么invoke就会被调用
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // 在被代理对象的目标方法执行之前，做些事情
        System.out.println(String.format("在被代理的目标方法执行前...目标方法名为:%s , 目标方法传递的参数为:%s ",method.getName(), Arrays.toString(args)));
        // 执行被代理对象的目标方法
        if(method.getName().equals("add")){
            System.out.println("针对指定方法做特殊处理演示：针对add方法，强制让第二个参数变为100");
            args[1] = 100;
        }
        Object res = method.invoke(obj, args); // 参数1 ：被代理的对象  参数2：要执行的方法

        // 在被代理对象的目标方法执行之后，做些事情
        System.out.println(String.format("在被代理的目标方法执行后...被执行的被代理对象为 %s",obj));

        return res;
    }
}